#include "ScnsSmctp.c.h"

#if defined(SCNS_SMCTP_ENABLE)&&SCNS_SMCTP_ENABLE==1

#include <time.h>
#include "ScnsYing.h"
#include "ScnsMenu.h"
#include "ScnsMath.h"
#include "ScnsLed.h"
#include "usart.h"
#include "ScnsMem.h"

static void handleDebug(const uint8*bufIn,uint32 length)
{
    static uint8 last=0;

    static int errCnt,totCnt;
    for(uint32 i=0;i<length;++i)
    {
        const uint8 same=((uint8)(last+1))==bufIn[i];
        errCnt+=!same;
        ++totCnt;
        if(!same||totCnt%1024==0)
        {
            hxPrintfFL("err %d tot %d %#02X %#02X\n",errCnt,totCnt,last,bufIn[i]);
        }
        last=bufIn[i];
    }
}

const ScnsSmctpChanelConfig scnsSmctpChanelConfig[SCNS_SMCTP_CHANNEL_MAX]={
    [SCNS_SMCTP_CHANNEL_DEBUG]={.callback=handleDebug},
    [SCNS_SMCTP_CHANNEL_MENU]={.callback=scnsMenuReceiveInterrupt},
};

ScnsStatus scnsBspSmctpCanSend(void)
{
    return SCNS_STATUS_OK;
}

#if defined(LL_RTC_FORMAT_BIN)

void scnsBspSmctpReceiveAbsoluteTime(uint64 us)
{
    time_t lt=us/1000000;
    struct tm*ptr=localtime(&lt);
    LL_RTC_TimeTypeDef rtcTime={.Hours=ptr->tm_hour,.Minutes=ptr->tm_min,.Seconds=ptr->tm_sec};
    LL_RTC_DateTypeDef rtcDate={.Day=ptr->tm_mday,.Month=ptr->tm_mon+1,.Year=(ptr->tm_year+1900)%100};
    LL_RTC_TIME_Init(RTC,LL_RTC_FORMAT_BIN,&rtcTime);
    LL_RTC_DATE_Init(RTC,LL_RTC_FORMAT_BIN,&rtcDate);
}

#endif

static __always_inline void ledToggle(void)
{
#if defined(SMCTP_LED)
    scnsLedToggleAfter(SMCTP_LED,25*1000);
#endif
}

#if defined(SMCTP_DMA_UART)
#define DMA_SetPeriphAddress            LL_DMA_SetPeriphAddress
#define DMA_SetMemoryAddress            LL_DMA_SetMemoryAddress
#define DMA_EnableStream                LL_DMA_EnableStream
#define DMA_EnableIT_TC                 LL_DMA_EnableIT_TC
#define DMA_EnableIT_TE                 LL_DMA_EnableIT_TE
#define DMA_EnableIT_HT                 LL_DMA_EnableIT_HT
#define DMA_SetDataLength               LL_DMA_SetDataLength
#define DMA_GetDataLength               LL_DMA_GetDataLength
#define USART_DMA_GetRegAddr            LL_USART_DMA_GetRegAddr
#define USART_DMA_REG_DATA_TRANSMIT     LL_USART_DMA_REG_DATA_TRANSMIT
#define USART_DMA_REG_DATA_RECEIVE      LL_USART_DMA_REG_DATA_RECEIVE
#define USART_EnableDMAReq_TX           LL_USART_EnableDMAReq_TX
#define USART_EnableDMAReq_RX           LL_USART_EnableDMAReq_RX
#define USART_EnableIT_IDLE             LL_USART_EnableIT_IDLE
#define USART_ClearFlag_TC              LL_USART_ClearFlag_TC
#endif
#if defined(SMCTP_BDMA_LPUART)
#define DMA_SetPeriphAddress            LL_BDMA_SetPeriphAddress
#define DMA_SetMemoryAddress            LL_BDMA_SetMemoryAddress
#define DMA_EnableStream                LL_BDMA_EnableChannel
#define DMA_EnableIT_TC                 LL_BDMA_EnableIT_TC
#define DMA_EnableIT_TE                 LL_BDMA_EnableIT_TE
#define DMA_EnableIT_HT                 LL_BDMA_EnableIT_HT
#define DMA_SetDataLength               LL_BDMA_SetDataLength
#define DMA_GetDataLength               LL_BDMA_GetDataLength
#define USART_DMA_GetRegAddr            LL_LPUART_DMA_GetRegAddr
#define USART_DMA_REG_DATA_TRANSMIT     LL_LPUART_DMA_REG_DATA_TRANSMIT
#define USART_DMA_REG_DATA_RECEIVE      LL_LPUART_DMA_REG_DATA_RECEIVE
#define USART_EnableDMAReq_TX           LL_LPUART_EnableDMAReq_TX
#define USART_EnableDMAReq_RX           LL_LPUART_EnableDMAReq_RX
#define USART_EnableIT_IDLE             LL_LPUART_EnableIT_IDLE
#define USART_ClearFlag_TC              LL_LPUART_ClearFlag_TC
#endif

ScnsStatus scnsBspSmctpInit(void)
{
    DMA_SetPeriphAddress(SMCTP_TX_DMAX,SMCTP_TX_DMACH,USART_DMA_GetRegAddr(SMCTP_UART,USART_DMA_REG_DATA_TRANSMIT));
    DMA_EnableIT_TC(SMCTP_TX_DMAX,SMCTP_TX_DMACH);
    DMA_EnableIT_TE(SMCTP_TX_DMAX,SMCTP_TX_DMACH);
    USART_EnableDMAReq_TX(SMCTP_UART);

    DMA_SetPeriphAddress(SMCTP_RX_DMAX,SMCTP_RX_DMACH,USART_DMA_GetRegAddr(SMCTP_UART,USART_DMA_REG_DATA_RECEIVE));
    DMA_EnableIT_TC(SMCTP_RX_DMAX,SMCTP_RX_DMACH);
    DMA_EnableIT_HT(SMCTP_RX_DMAX,SMCTP_RX_DMACH);
    DMA_EnableIT_TE(SMCTP_RX_DMAX,SMCTP_RX_DMACH);
    USART_EnableDMAReq_RX(SMCTP_UART);
    USART_EnableIT_IDLE(USART1);
    return SCNS_STATUS_OK;
}

ScnsStatus scnsBspSmctpSend(const uint8*buf,uint16*len)
{
    ledToggle();
    DMA_SetDataLength(SMCTP_TX_DMAX,SMCTP_TX_DMACH,*len);
    DMA_SetMemoryAddress(SMCTP_TX_DMAX,SMCTP_TX_DMACH,(uintptr_t)buf);
    USART_ClearFlag_TC(SMCTP_UART);
    DMA_EnableStream(SMCTP_TX_DMAX,SMCTP_TX_DMACH);
    return SCNS_STATUS_OK;
}

ScnsStatus scnsBspSmctpReceiveDmaStart(uint8*buf,uint16 len)
{
    ledToggle();
    DMA_SetDataLength(SMCTP_RX_DMAX,SMCTP_RX_DMACH,len);
    DMA_SetMemoryAddress(SMCTP_RX_DMAX,SMCTP_RX_DMACH,(uintptr_t)buf);
    DMA_EnableStream(SMCTP_RX_DMAX,SMCTP_RX_DMACH);
    return SCNS_STATUS_OK;
}

int32 scnsBspSmctpReceiveDmaGetLength(void)
{
    ledToggle();
    return -(DMA_GetDataLength(SMCTP_RX_DMAX,SMCTP_RX_DMACH));
}

void scnsBspSmctpFinishInit(void)
{
    hxPrintfFL("Compile at " __DATE__ " " __TIME__ "\n");
    uint8 clear=0;

    //@formatter:off
    if(0){}
#if defined(STM32H7)
#define check(clr,name,cond)    else if(((RCC->RSR)&(cond))==(cond)){hxPrintfFL("Reset by " name "\n");clear=clr;}
    check(1,"pwr_por_rst",RCC_RSR_PORRSTF|RCC_RSR_PINRSTF|RCC_RSR_BORRSTF|RCC_RSR_D2RSTF|RCC_RSR_D1RSTF|RCC_RSR_CPURSTF)
    check(0,"SFTREST",RCC_RSR_SFTRSTF|RCC_RSR_PINRSTF|RCC_RSR_CPURSTF)
    check(1,"IWDG1",RCC_RSR_IWDG1RSTF|RCC_RSR_PINRSTF|RCC_RSR_CPURSTF)
    check(0,"NRST",RCC_RSR_PINRSTF|RCC_RSR_CPURSTF)
    check(0,"CPUREST",RCC_RSR_CPURSTF)
    else{hxPrintfFL("Reset by %#010X\n",RCC->RSR);}
    RCC->RSR|=RCC_RSR_RMVF;
#elif defined(STM32F7)
#define check(clr,name,cond)    else if(((RCC->CSR)&(cond))==(cond)){hxPrintfFL("Reset by " name "\n");clear=clr;}
    check(1,"pwr_por_rst",RCC_CSR_PORRSTF|RCC_CSR_PINRSTF|RCC_CSR_BORRSTF)
    check(0,"SFTREST",RCC_CSR_SFTRSTF|RCC_CSR_PINRSTF)
    check(1,"IWDG1",RCC_CSR_IWDGRSTF|RCC_CSR_PINRSTF)
    check(0,"NRST",RCC_CSR_PINRSTF)
    else{hxPrintfFL("Reset by %#010X\n",RCC->CSR);}
    RCC->CSR|=RCC_CSR_RMVF;
#else
#error
#endif
    //@formatter:on

    {
#if defined(STM32H7)||defined(STM32F7)
        volatile static uint32 RAM_BACKUP_RESET_INFO[9];
        if(clear){for(uint8 i=0;i<sizeof(RAM_BACKUP_RESET_INFO)/sizeof(RAM_BACKUP_RESET_INFO[0]);++i){RAM_BACKUP_RESET_INFO[i]=0;}}
        if(RAM_BACKUP_RESET_INFO[0]||RAM_BACKUP_RESET_INFO[1]||RAM_BACKUP_RESET_INFO[2])
        {
            const char*file=((const char*)RAM_BACKUP_RESET_INFO[0]?(const char*)RAM_BACKUP_RESET_INFO[0]:"");
            const char*code=((const char*)RAM_BACKUP_RESET_INFO[2]?(const char*)RAM_BACKUP_RESET_INFO[2]:"");
            hxPrintfFL("Last reset at file %s line %u code %s\n",file,RAM_BACKUP_RESET_INFO[1],code);
            RAM_BACKUP_RESET_INFO[0]=RAM_BACKUP_RESET_INFO[1]=RAM_BACKUP_RESET_INFO[2]=0;
        }
#else
#error
#endif
    }
    {
#if defined(STM32H7)||defined(STM32F7)
        volatile static uint32 RAM_BACKUP_RESET_INFO[9];
#define P(i,n)  do{if(RAM_BACKUP_RESET_INFO[i]){hxPrintfFL("SCB->%-5s : %#010X\n",#n,RAM_BACKUP_RESET_INFO[i]);RAM_BACKUP_RESET_INFO[i]=0;}}while(0)
        P(3,MMFAR);
        P(4,BFAR);
        P(5,CFSR);
        P(6,HFSR);
        P(7,DFSR);
        P(8,ABFSR);
#else
#error
#endif
    }
}

#endif
